﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Diagnostics;

namespace JN.Utility 
{
    public enum SingleRGB
    {
        R,
        G,
        B
    }

    public enum GrayType
    {
        R,
        G,
        B,
        RGB
    }
    

    public static class ImagesConverter
    {

        public static Color DRAW_BG_COLOR = Color.FromArgb(255, 0, 0, 0); //背景色 黑色
        public static Color DRAW_COPPER_COLOR = Color.Orange;// Color.FromArgb(255, 255, 255, 0); //铜颜色 黄色
        public static Color DRAW_GLEAM_COLOR = Color.Bisque;
        public static Color DRAW_DRILL_COLOR = Color.FromArgb(255, 0, 255, 0); //孔颜色 绿色

        /// <summary>
        /// GRB24位图转8位灰度索引图。性能一般
        /// </summary>
        /// <param name="bitmapSource"></param>
        /// <returns></returns>
        public static Image ConvertToGray8bppIndexed1(this Image bitmapSource)
        {
            try
            {
                int height = bitmapSource.Height;
                int width = bitmapSource.Width;

                Bitmap bitmap8 = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
                Bitmap bitmap24 = (Bitmap)bitmapSource;

                BitmapData data24 = bitmap24.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                IntPtr ptr24 = data24.Scan0;
                BitmapData data8 = bitmap8.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
                IntPtr ptr8 = data8.Scan0;

                int byteColor = data24.Stride * data24.Height;
                byte[] colorValues = new byte[byteColor];
                System.Runtime.InteropServices.Marshal.Copy(ptr24, colorValues, 0, byteColor);
                int byte8 = data8.Stride * data8.Height;
                byte[] gray8Values = new byte[byte8];
                System.Runtime.InteropServices.Marshal.Copy(ptr8, gray8Values, 0, byte8);

                int r, g, b;
                for (int i = 0, n = 0; i < byteColor; i += 3, n++)
                {
                    r = colorValues[i + 2];
                    g = colorValues[i + 1];
                    b = colorValues[i];

                    gray8Values[n] = (byte)((int)(0.299 * r) + (int)(0.587 * g) + (int)(0.114 * b));
                }
                System.Runtime.InteropServices.Marshal.Copy(gray8Values, 0, ptr8, byte8);
                System.Runtime.InteropServices.Marshal.Copy(colorValues, 0, ptr24, byteColor);

                bitmap24.UnlockBits(data24);
                bitmap24.Dispose();
                bitmap8.UnlockBits(data8);

                ColorPalette palette = bitmap8.Palette;
                for (int i = 0; i != palette.Entries.Length; i++)
                {
                    palette.Entries[i] = Color.FromArgb(i, i, i);
                }
                bitmap8.Palette = palette;

                return bitmap8;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// GRB24位图转8位灰度索引图。性能快
        /// </summary>
        /// <param name="bitmapSource"></param>
        /// <returns></returns>
        public static Image ConvertToGray8bppIndexed2(this Image bitmapSource)
        {
            try
            {
                int width = bitmapSource.Width;
                int height = bitmapSource.Height;
                Bitmap bitmap8 = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
                Bitmap bitmap24 = new Bitmap(width, height, PixelFormat.Format24bppRgb);
                {
                    Graphics g = Graphics.FromImage(bitmap24);
                    g.PageUnit = GraphicsUnit.Pixel;
                    g.DrawImage(bitmapSource, 0, 0, width, height);
                    g.Dispose();
                }
                Rectangle rect = new Rectangle(0, 0, width, height);
                BitmapData bitmapData8 = bitmap8.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
                IntPtr ptr8 = bitmapData8.Scan0;
                BitmapData bitmapData24 = bitmap24.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                unsafe
                {
                    byte* bits;
                    if (bitmapData8.Stride > 0)
                        bits = (byte*)ptr8.ToPointer();
                    else
                        bits = (byte*)ptr8.ToPointer() + bitmapData8.Stride * (height - 1);
                    uint stride = (uint)Math.Abs(bitmapData8.Stride);
                    byte* p = (byte*)bitmapData24.Scan0;

                    for (uint row = 0; row < height; ++row)
                    {
                        byte* line8 = bits + row * stride;
                        byte* line24 = p + row * bitmapData24.Stride;
                        for (uint col = 0; col < width; ++col)
                        {
                            *(line8) = (byte)(0.114 * (*(line24 + 0)) + 0.587 * (*(line24 + 1)) + 0.299 * (*(line24 + 2)));
                            line8++;
                            line24 += 3;
                        }
                    }
                    bitmap24.UnlockBits(bitmapData24);
                }
                bitmap8.UnlockBits(bitmapData8);
                bitmap24.Dispose();

                ColorPalette palette = bitmap8.Palette;
                for (int i = 0; i != palette.Entries.Length; i++)
                {
                    palette.Entries[i] = Color.FromArgb(i, i, i);
                }
                bitmap8.Palette = palette;

                return bitmap8;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 最快的32位彩图转32位灰度图
        /// </summary>
        /// <param name="original"></param>
        /// <returns></returns>
        public static Image MakeGrayscale(this Image original)
        {
            //create a blank bitmap the same size as original
            Bitmap newBitmap = new Bitmap(original.Width, original.Height);

            //get a graphics object from the new image
            Graphics g = Graphics.FromImage(newBitmap);

            //create the grayscale ColorMatrix
            ColorMatrix colorMatrix = new ColorMatrix(
               new float[][]
              {
                 new float[] {.3f, .3f, .3f, 0, 0},
                 new float[] {.59f, .59f, .59f, 0, 0},
                 new float[] {.11f, .11f, .11f, 0, 0},
                 new float[] {0, 0, 0, 1, 0},
                 new float[] {0, 0, 0, 0, 1}
              });

            //create some image attributes
            ImageAttributes attributes = new ImageAttributes();

            //set the color matrix attribute
            attributes.SetColorMatrix(colorMatrix);

            //draw the original image on the new image
            //using the grayscale color matrix
            g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
               0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);

            //dispose the Graphics object
            g.Dispose();
            return newBitmap;
        }

        /// <summary>
        /// GRB24位图转8位灰度索引图
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        public static Image ConvertTo8bpp(this Image image)
        {
            try
            {
                int width = image.Width;
                int height = image.Height;
                Bitmap bitmap8 = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
                Bitmap bitmap24 = new Bitmap(width, height, PixelFormat.Format24bppRgb);
                {
                    Graphics g = Graphics.FromImage(bitmap24);
                    g.PageUnit = GraphicsUnit.Pixel;
                    g.DrawImage(image, 0, 0, width, height);
                    g.Dispose();
                }
                Rectangle rect = new Rectangle(0, 0, width, height);
                BitmapData bitmapData8 = bitmap8.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
                IntPtr ptr8 = bitmapData8.Scan0;
                BitmapData bitmapData24 = bitmap24.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                unsafe
                {
                    byte* bits;
                    if (bitmapData8.Stride > 0)
                        bits = (byte*)ptr8.ToPointer();
                    else
                        bits = (byte*)ptr8.ToPointer() + bitmapData8.Stride * (height - 1);
                    uint stride = (uint)Math.Abs(bitmapData8.Stride);

                    byte* p = (byte*)bitmapData24.Scan0;

                    byte br = DRAW_BG_COLOR.R;
                    byte bg = DRAW_BG_COLOR.G;
                    byte bb = DRAW_BG_COLOR.B;
                    byte cr = DRAW_COPPER_COLOR.R;
                    byte cg = DRAW_COPPER_COLOR.G;
                    byte cb = DRAW_COPPER_COLOR.B;
                    byte dr = DRAW_DRILL_COLOR.R;
                    byte dg = DRAW_DRILL_COLOR.G;
                    byte db = DRAW_DRILL_COLOR.B;
                    for (uint row = 0; row < height; ++row)
                    {
                        byte* line8 = bits + row * stride;
                        byte* line24 = p + row * bitmapData24.Stride;
                        for (uint col = 0; col < width; ++col)
                        {
                            //DRAW_BG_COLOR = Color.FromArgb(255, 0, 0, 0); //背景色 黑色
                            //DRAW_COPPER_COLOR = Color.FromArgb(255, 255, 255, 0); //铜颜色 黄色
                            //DRAW_DRILL_COLOR = Color.FromArgb(255, 0, 255, 0); //孔颜色 绿色
                            byte b = 0;
                            if (p[0] == bb && p[1] == bg && p[2] == br) b = 0;
                            else if (p[0] == cb && p[1] == cg && p[2] == cr) b = 255;
                            else if (p[0] == db && p[1] == dg && p[2] == dr) b = 128;

                            *(line8) = b;
                            line8++;
                            line24 += 3;
                        }
                    }
                    bitmap24.UnlockBits(bitmapData24);
                }
                bitmap8.UnlockBits(bitmapData8);
                bitmap24.Dispose();

                ColorPalette palette = bitmap8.Palette;
                for (int i = 0; i != palette.Entries.Length; i++)
                {
                    palette.Entries[i] = Color.FromArgb(i, i, i);
                }
                bitmap8.Palette = palette;

                return bitmap8;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 24位图转8位RGB单色通道索引图
        /// </summary>
        /// <param name="image"></param>
        /// <param name="rgb"></param>
        /// <returns></returns>
        public static Image ConvertToSingleRGB(this Image image, SingleRGB rgb)
        {
            try
            {
                int width = image.Width;
                int height = image.Height;
                Bitmap bitmap8 = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
                Bitmap bitmap24 = new Bitmap(width, height, PixelFormat.Format24bppRgb);
                {
                    Graphics g = Graphics.FromImage(bitmap24);
                    g.PageUnit = GraphicsUnit.Pixel;
                    g.DrawImage(image, 0, 0, width, height);
                    g.Dispose();
                }
                Rectangle rect = new Rectangle(0, 0, width, height);
                BitmapData bitmapData8 = bitmap8.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
                IntPtr ptr8 = bitmapData8.Scan0;
                BitmapData bitmapData24 = bitmap24.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                unsafe
                {
                    byte* bits;
                    if (bitmapData8.Stride > 0)
                        bits = (byte*)ptr8.ToPointer();
                    else
                        bits = (byte*)ptr8.ToPointer() + bitmapData8.Stride * (height - 1);
                    uint stride = (uint)Math.Abs(bitmapData8.Stride);

                    byte* p = (byte*)bitmapData24.Scan0;

                    for (uint row = 0; row < height; ++row)
                    {
                        byte* line8 = bits + row * stride;
                        byte* line24 = p + row * bitmapData24.Stride;
                        for (uint col = 0; col < width; ++col)
                        {
                            switch (rgb)
                            {
                                case SingleRGB.R:
                                    *(line8) = *(line24 + 2);
                                    break;
                                case SingleRGB.G:
                                    *(line8) = *(line24 + 1);
                                    break;
                                case SingleRGB.B:
                                    *(line8) = *(line24 + 0);
                                    break;
                                default:
                                    break;
                            }
                            line8++;
                            line24 += 3;
                        }
                    }
                    bitmap24.UnlockBits(bitmapData24);
                }
                bitmap8.UnlockBits(bitmapData8);
                bitmap24.Dispose();

                ColorPalette palette = bitmap8.Palette;
                for (int i = 0; i != palette.Entries.Length; i++)
                {
                    palette.Entries[i] = Color.FromArgb(i, i, i);
                }
                bitmap8.Palette = palette;

                return bitmap8;
            }
            catch
            {
                return null;
            }
        }

        public static Image MakeThumbnail(this Image originalImage, Size thumbnailSize)
        {
            System.Diagnostics.Debug.Assert(originalImage != null && thumbnailSize != null);
            System.Diagnostics.Debug.Assert(thumbnailSize.Width > 0 && thumbnailSize.Height > 0);

            Image bitmap = new Bitmap(thumbnailSize.Width, thumbnailSize.Height);

            using (Graphics g = Graphics.FromImage(bitmap))
            {
                g.InterpolationMode = InterpolationMode.High;
                g.SmoothingMode = SmoothingMode.HighQuality;
                g.DrawImage(originalImage, new Rectangle(0, 0, thumbnailSize.Width, thumbnailSize.Height),
                    new Rectangle(0, 0, originalImage.Width, originalImage.Height), GraphicsUnit.Pixel);
            }

            return bitmap;
        }


        //求24位位图指定的区域的平均灰度。
        public static unsafe int CalcAreaGrayValue(this Image bitmapSource, Rectangle area)
        {
            int grayValue = 0;
            decimal decGrayValue = 0;
            
            Bitmap bitmap24 = (Bitmap)bitmapSource;
            if (bitmap24.PixelFormat != PixelFormat.Format24bppRgb)
            {
                throw new ArgumentException("PixelFormat != Format24bppRgb");  
            }


            BitmapData data24 = bitmap24.LockBits(area, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            IntPtr ptr24 = data24.Scan0; 

            int bytesArea = data24.Stride * data24.Height;
            int r, g, b;

            byte* pArea = (byte*)ptr24.ToPointer();
            int n = 0;
            for (int i = 0 ; i < bytesArea; i += 3, n++)
            {
                r = pArea[i + 2];
                g = pArea[i + 1];
                b = pArea[i];
                decGrayValue += ( (int)(0.299 * r) + (int)(0.587 * g) + (int)(0.114 * b) ); 
            }

            bitmap24.UnlockBits(data24);
            grayValue = (int)Math.Round(decGrayValue / n ); 

            Debug.Assert(grayValue >= 0 && grayValue <= 255);

            return grayValue; 
        }


        //求24位位图指定的区域的平均灰度。
        public static unsafe int[] CalcAreaHistogramData(this Image bitmapSource, Rectangle area,GrayType gType)
        {
 
            int[] histogramData = new int[256];

            Bitmap bitmap24 = (Bitmap)bitmapSource;
            if (bitmap24.PixelFormat != PixelFormat.Format24bppRgb)
            {
                throw new ArgumentException("PixelFormat != Format24bppRgb");
            }


            BitmapData data24 = bitmap24.LockBits(area, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            IntPtr ptr24 = data24.Scan0;

            int bytesArea = data24.Stride * data24.Height;
            int r, g, b,rgb;

            byte* pArea = (byte*)ptr24.ToPointer();

            for (int y = 0; y < data24.Height; y++)
            {
                for (int x = 0; x < data24.Width; x++)
                {
                    r = *(pArea + y * data24.Stride + x * 3 + 2);
                    g = *(pArea + y * data24.Stride + x * 3 + 1);
                    b = *(pArea + y * data24.Stride + x * 3 + 0);
                    rgb = ((int)(0.299 * r) + (int)(0.587 * g) + (int)(0.114 * b));
                    if (rgb > 255)
                    {
                        rgb = 255;
                    }
                    if (gType == GrayType.R)
                    {
                        histogramData[r]++;
                    }
                    else if (gType == GrayType.G)
                    {
                        histogramData[g]++;
                    }
                    else if (gType == GrayType.B)
                    {
                        histogramData[b]++;
                    }
                    else
                    {
                        histogramData[rgb]++;
                    }
                }
            }

            bitmap24.UnlockBits(data24);  
            return histogramData;
        }

        public static unsafe Bitmap Convert8To24Bmp(this Image img) 
        {
            Bitmap bitmap24 = null;
            Bitmap bitmap8 = (Bitmap)img;
            if (bitmap8 == null || bitmap8.PixelFormat != PixelFormat.Format8bppIndexed) 
            {
                return bitmap24;
            }


            bitmap24 = new Bitmap(bitmap8.Width, bitmap8.Height, PixelFormat.Format24bppRgb);

            BitmapData bmData8 = bitmap8.LockBits(new Rectangle(0, 0, bitmap8.Width, bitmap8.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
            BitmapData bmData24 = bitmap24.LockBits(new Rectangle(0, 0, bitmap24.Width, bitmap24.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); 

            byte* pBM8 = (byte*)bmData8.Scan0.ToPointer();
            byte* pBM24 = (byte*)bmData24.Scan0.ToPointer(); 
            for (int y = 0; y < bitmap8.Height; y++)
            {
                byte* pBM24RowAddress =  pBM24 + y * bmData24.Stride;
                byte* pBM8RowAddress = pBM8 + y * bmData8.Stride; 
                for (int x = 0; x < bitmap8.Width; x++)
                {
                    byte gray = (byte)(*(pBM8RowAddress + x)); 
                    *(pBM24RowAddress + x * 3) = gray;
                    *(pBM24RowAddress + x * 3 + 1) = gray;
                    *(pBM24RowAddress + x * 3 + 2) = gray; 
                }
            }

            bitmap24.UnlockBits(bmData24); 
            bitmap8.UnlockBits(bmData8); 
            return bitmap24; 
        }

    }

}
